﻿// Inner Fire 游戏引擎库
// 场景分装 - 房间
//
// Copyright (c) 2023 by 尔西大帝. All rights reserved.
//
// 这个类分装各种场景的构建（都在一个文件里面太大了）
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-25

#include <dx_graphics.h>

namespace ifire {
void DxGraphics::Room_Update() {
  OnKeyboardInput_Room();
  UpdateObjectCBs();
  UpdateMaterialCBs();
  UpdateMainPassCB();
  UpdateReflectedPassCB();
}

void DxGraphics::Room_InitPipeline() {
  LoadTextures_Room();

  BuildRootSignature();
  BuildDescriptorHeaps_Room();
  BuildShaderAndInputLayout();

  BuildMesh_Room();
  BuildMesh_Skull();
  BuildMaterials_Room();
  BuildRenderItems_Room();

  BuildFrameResources_Room();
  BuildPSOs_Room();
}

void DxGraphics::LoadTextures_Room() {
  auto bricksTex = std::make_unique<Texture>();
  bricksTex->Name = "BricksTex";
  bricksTex->Filename = "Textures/bricks3.dds";
  HASSERT(DirectX::CreateDDSTextureFromFile12(device_.Get(),
      command_list_.Get(), AnsiToWString(bricksTex->Filename).c_str(),
      bricksTex->Resource, bricksTex->UploadHeap));

  auto checkboardTex = std::make_unique<Texture>();
  checkboardTex->Name = "CheckboardTex";
  checkboardTex->Filename = "Textures/checkboard.dds";
  HASSERT(DirectX::CreateDDSTextureFromFile12(device_.Get(),
      command_list_.Get(), AnsiToWString(checkboardTex->Filename).c_str(),
      checkboardTex->Resource, checkboardTex->UploadHeap));

  auto iceTex = std::make_unique<Texture>();
  iceTex->Name = "IceTex";
  iceTex->Filename = "Textures/ice.dds";
  HASSERT(DirectX::CreateDDSTextureFromFile12(device_.Get(),
      command_list_.Get(), AnsiToWString(iceTex->Filename).c_str(),
      iceTex->Resource, iceTex->UploadHeap));

  auto white1x1Tex = std::make_unique<Texture>();
  white1x1Tex->Name = "White1x1Tex";
  white1x1Tex->Filename = "Textures/white1x1.dds";
  HASSERT(DirectX::CreateDDSTextureFromFile12(device_.Get(),
      command_list_.Get(), AnsiToWString(white1x1Tex->Filename).c_str(),
      white1x1Tex->Resource, white1x1Tex->UploadHeap));

  textures_[bricksTex->Name] = std::move(bricksTex);
  textures_[checkboardTex->Name] = std::move(checkboardTex);
  textures_[iceTex->Name] = std::move(iceTex);
  textures_[white1x1Tex->Name] = std::move(white1x1Tex);
}

void DxGraphics::BuildMaterials_Room() {
  auto bricks = std::make_unique<Material>();
  bricks->Name = "Bricks";
  bricks->MatCBIndex = 0;
  bricks->DiffuseSrvHeapIndex = 0;
  bricks->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  bricks->FresnelR0 = XMFLOAT3(0.05f, 0.05f, 0.05f);
  bricks->Roughness = 0.25f;

  auto checkertile = std::make_unique<Material>();
  checkertile->Name = "Checkertile";
  checkertile->MatCBIndex = 1;
  checkertile->DiffuseSrvHeapIndex = 1;
  checkertile->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  checkertile->FresnelR0 = XMFLOAT3(0.07f, 0.07f, 0.07f);
  checkertile->Roughness = 0.3f;

  auto icemirror = std::make_unique<Material>();
  icemirror->Name = "Icemirror";
  icemirror->MatCBIndex = 2;
  icemirror->DiffuseSrvHeapIndex = 2;
  icemirror->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 0.3f);
  icemirror->FresnelR0 = XMFLOAT3(0.1f, 0.1f, 0.1f);
  icemirror->Roughness = 0.5f;

  auto skullMat = std::make_unique<Material>();
  skullMat->Name = "SkullMat";
  skullMat->MatCBIndex = 3;
  skullMat->DiffuseSrvHeapIndex = 3;
  skullMat->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  skullMat->FresnelR0 = XMFLOAT3(0.05f, 0.05f, 0.05f);
  skullMat->Roughness = 0.3f;

  auto shadowMat = std::make_unique<Material>();
  shadowMat->Name = "ShadowMat";
  shadowMat->MatCBIndex = 4;
  shadowMat->DiffuseSrvHeapIndex = 3;
  shadowMat->DiffuseAlbedo = XMFLOAT4(0.0f, 0.0f, 0.0f, 0.5f);
  shadowMat->FresnelR0 = XMFLOAT3(0.001f, 0.001f, 0.001f);
  shadowMat->Roughness = 0.0f;

  materials_["Bricks"] = std::move(bricks);
  materials_["Checkertile"] = std::move(checkertile);
  materials_["Icemirror"] = std::move(icemirror);
  materials_["SkullMat"] = std::move(skullMat);
  materials_["ShadowMat"] = std::move(shadowMat);
}

void DxGraphics::BuildDescriptorHeaps_Room() {
  //
  // Create the SRV heap.
  //
  D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
  srvHeapDesc.NumDescriptors = 4;
  srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
  srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
  HASSERT(device_->CreateDescriptorHeap(
      &srvHeapDesc, IID_PPV_ARGS(&srv_descriptor_heap_)));

  //
  // Fill out the heap with actual descriptors.
  //
  CD3DX12_CPU_DESCRIPTOR_HANDLE hDescriptor(
      srv_descriptor_heap_->GetCPUDescriptorHandleForHeapStart());

  auto bricksTex = textures_["BricksTex"]->Resource;
  auto checkboardTex = textures_["CheckboardTex"]->Resource;
  auto iceTex = textures_["IceTex"]->Resource;
  auto white1x1Tex = textures_["White1x1Tex"]->Resource;

  D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
  srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
  srvDesc.Format = bricksTex->GetDesc().Format;
  srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
  srvDesc.Texture2D.MostDetailedMip = 0;
  srvDesc.Texture2D.MipLevels = -1;
  device_->CreateShaderResourceView(bricksTex.Get(), &srvDesc, hDescriptor);

  // next descriptor
  hDescriptor.Offset(1, cbv_srv_uav_descriptor_size_);

  srvDesc.Format = checkboardTex->GetDesc().Format;
  device_->CreateShaderResourceView(checkboardTex.Get(), &srvDesc, hDescriptor);

  // next descriptor
  hDescriptor.Offset(1, cbv_srv_uav_descriptor_size_);

  srvDesc.Format = iceTex->GetDesc().Format;
  device_->CreateShaderResourceView(iceTex.Get(), &srvDesc, hDescriptor);

  // next descriptor
  hDescriptor.Offset(1, cbv_srv_uav_descriptor_size_);

  srvDesc.Format = white1x1Tex->GetDesc().Format;
  device_->CreateShaderResourceView(white1x1Tex.Get(), &srvDesc, hDescriptor);
}

void DxGraphics::BuildMesh_Room() {
  // Create and specify geometry.  For this sample we draw a floor
  // and a wall with a mirror on it.  We put the floor, wall, and
  // mirror geometry in one vertex buffer.
  //
  //   |--------------|
  //   |              |
  //   |----|----|----|
  //   |Wall|Mirr|Wall|
  //   |    | or |    |
  //   /--------------/
  //  /   Floor      /
  // /--------------/

  std::array<Vertex, 20> vertices = {
      // Floor: Observe we tile texture coordinates.
      Vertex(-3.5f, 0.0f, -10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 4.0f), // 0
      Vertex(-3.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f),
      Vertex(7.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 4.0f, 0.0f),
      Vertex(7.5f, 0.0f, -10.0f, 0.0f, 1.0f, 0.0f, 4.0f, 4.0f),

      // Wall: Observe we tile texture coordinates, and that we
      // leave a gap in the middle for the mirror.
      Vertex(-3.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 2.0f), // 4
      Vertex(-3.5f, 4.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f),
      Vertex(-2.5f, 4.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.5f, 0.0f),
      Vertex(-2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.5f, 2.0f),

      Vertex(2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 2.0f), // 8
      Vertex(2.5f, 4.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f),
      Vertex(7.5f, 4.0f, 0.0f, 0.0f, 0.0f, -1.0f, 2.0f, 0.0f),
      Vertex(7.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 2.0f, 2.0f),

      Vertex(-3.5f, 4.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f), // 12
      Vertex(-3.5f, 6.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f),
      Vertex(7.5f, 6.0f, 0.0f, 0.0f, 0.0f, -1.0f, 6.0f, 0.0f),
      Vertex(7.5f, 4.0f, 0.0f, 0.0f, 0.0f, -1.0f, 6.0f, 1.0f),

      // Mirror
      Vertex(-2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f), // 16
      Vertex(-2.5f, 4.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f),
      Vertex(2.5f, 4.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f),
      Vertex(2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f)};

  std::array<std::int16_t, 30> indices = {// Floor
      0, 1, 2, 0, 2, 3,

      // Walls
      4, 5, 6, 4, 6, 7,

      8, 9, 10, 8, 10, 11,

      12, 13, 14, 12, 14, 15,

      // Mirror
      16, 17, 18, 16, 18, 19};

  SubmeshGeometry floorSubmesh(6, 0, 0);
  SubmeshGeometry wallSubmesh(18, 6, 0);
  SubmeshGeometry mirrorSubmesh(6, 24, 0);

  const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);
  const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);

  auto geo = std::make_unique<MeshGeometry>();
  geo->Name = "RoomGeo";

  HASSERT(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
  CopyMemory(
      geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

  HASSERT(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  CopyMemory(
      geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

  geo->VertexBufferGPU = CreateDefaultBuffer(
      vertices.data(), vbByteSize, geo->VertexBufferUploader);

  geo->IndexBufferGPU =
      CreateDefaultBuffer(indices.data(), ibByteSize, geo->IndexBufferUploader);

  geo->VertexByteStride = sizeof(Vertex);
  geo->VertexBufferByteSize = vbByteSize;
  geo->IndexFormat = DXGI_FORMAT_R16_UINT;
  geo->IndexBufferByteSize = ibByteSize;

  geo->DrawArgs["Floor"] = floorSubmesh;
  geo->DrawArgs["Wall"] = wallSubmesh;
  geo->DrawArgs["Mirror"] = mirrorSubmesh;

  geometries_[geo->Name] = std::move(geo);
}

void DxGraphics::BuildMesh_Skull() {
  std::ifstream fin("Models/skull.txt");

  if (!fin) {
    MessageBox(0, "Models/skull.txt not found.", 0, 0);
    return;
  }

  UINT vcount = 0;
  UINT tcount = 0;
  std::string ignore;

  fin >> ignore >> vcount;
  fin >> ignore >> tcount;
  fin >> ignore >> ignore >> ignore >> ignore;

  std::vector<Vertex> vertices(vcount);
  for (UINT i = 0; i < vcount; ++i) {
    fin >> vertices[i].Pos.x >> vertices[i].Pos.y >> vertices[i].Pos.z;
    fin >> vertices[i].Normal.x >> vertices[i].Normal.y >> vertices[i].Normal.z;

    // Model does not have texture coordinates, so just zero them out.
    vertices[i].TexC = {0.0f, 0.0f};
  }

  fin >> ignore;
  fin >> ignore;
  fin >> ignore;

  std::vector<std::int32_t> indices(3 * tcount);
  for (UINT i = 0; i < tcount; ++i) {
    fin >> indices[i * 3 + 0] >> indices[i * 3 + 1] >> indices[i * 3 + 2];
  }

  fin.close();

  //
  // Pack the indices of all the meshes into one index buffer.
  //

  const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);

  const UINT ibByteSize = (UINT)indices.size() * sizeof(std::int32_t);

  auto geo = std::make_unique<MeshGeometry>();
  geo->Name = "SkullGeo";

  HASSERT(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
  CopyMemory(
      geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

  HASSERT(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  CopyMemory(
      geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

  geo->VertexBufferGPU = CreateDefaultBuffer(
      vertices.data(), vbByteSize, geo->VertexBufferUploader);

  geo->IndexBufferGPU =
      CreateDefaultBuffer(indices.data(), ibByteSize, geo->IndexBufferUploader);

  geo->VertexByteStride = sizeof(Vertex);
  geo->VertexBufferByteSize = vbByteSize;
  geo->IndexFormat = DXGI_FORMAT_R32_UINT;
  geo->IndexBufferByteSize = ibByteSize;

  SubmeshGeometry submesh((UINT)indices.size(), 0, 0);

  geo->DrawArgs["Skull"] = submesh;

  geometries_[geo->Name] = std::move(geo);
}

void DxGraphics::BuildRenderItems_Room() {
  auto floorRitem = std::make_unique<RenderItem>();
  floorRitem->World = MathHelper::Identity4x4();
  floorRitem->TexTransform = MathHelper::Identity4x4();
  floorRitem->ObjCBIndex = 0;
  floorRitem->Mat = materials_["Checkertile"].get();
  floorRitem->Geo = geometries_["RoomGeo"].get();
  floorRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  floorRitem->IndexCount = floorRitem->Geo->DrawArgs["Floor"].IndexCount;
  floorRitem->StartIndexLocation =
      floorRitem->Geo->DrawArgs["Floor"].StartIndexLocation;
  floorRitem->BaseVertexLocation =
      floorRitem->Geo->DrawArgs["Floor"].BaseVertexLocation;
  render_layer[(int)RenderLayer::Opaque].push_back(floorRitem.get());

  auto wallsRitem = std::make_unique<RenderItem>();
  wallsRitem->World = MathHelper::Identity4x4();
  wallsRitem->TexTransform = MathHelper::Identity4x4();
  wallsRitem->ObjCBIndex = 1;
  wallsRitem->Mat = materials_["Bricks"].get();
  wallsRitem->Geo = geometries_["RoomGeo"].get();
  wallsRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  wallsRitem->IndexCount = wallsRitem->Geo->DrawArgs["Wall"].IndexCount;
  wallsRitem->StartIndexLocation =
      wallsRitem->Geo->DrawArgs["Wall"].StartIndexLocation;
  wallsRitem->BaseVertexLocation =
      wallsRitem->Geo->DrawArgs["Wall"].BaseVertexLocation;
  render_layer[(int)RenderLayer::Opaque].push_back(wallsRitem.get());

  auto skullRitem = std::make_unique<RenderItem>();
  skullRitem->World = MathHelper::Identity4x4();
  skullRitem->TexTransform = MathHelper::Identity4x4();
  skullRitem->ObjCBIndex = 2;
  skullRitem->Mat = materials_["SkullMat"].get();
  skullRitem->Geo = geometries_["SkullGeo"].get();
  skullRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  skullRitem->IndexCount = skullRitem->Geo->DrawArgs["Skull"].IndexCount;
  skullRitem->StartIndexLocation =
      skullRitem->Geo->DrawArgs["Skull"].StartIndexLocation;
  skullRitem->BaseVertexLocation =
      skullRitem->Geo->DrawArgs["Skull"].BaseVertexLocation;
  skull_item_ = skullRitem.get();
  render_layer[(int)RenderLayer::Opaque].push_back(skullRitem.get());

  // Reflected skull will have different world matrix, so it needs to be its own
  // render item.
  auto reflectedSkullRitem = std::make_unique<RenderItem>();
  *reflectedSkullRitem = *skullRitem;
  reflectedSkullRitem->ObjCBIndex = 3;
  reflected_skull_item_ = reflectedSkullRitem.get();
  render_layer[(int)RenderLayer::Reflected].push_back(
      reflectedSkullRitem.get());

  auto reflectedFloor = std::make_unique<RenderItem>();
  *reflectedFloor = *floorRitem;
  reflectedFloor->ObjCBIndex = 4;
  reflected_floor_item_ = reflectedFloor.get();
  render_layer[(int)RenderLayer::Reflected].push_back(reflectedFloor.get());

  // Shadowed skull will have different world matrix, so it needs to be its own
  // render item.
  auto shadowedSkullRitem = std::make_unique<RenderItem>();
  *shadowedSkullRitem = *skullRitem;
  shadowedSkullRitem->ObjCBIndex = 5;
  shadowedSkullRitem->Mat = materials_["ShadowMat"].get();
  shadowed_skull_item_ = shadowedSkullRitem.get();
  render_layer[(int)RenderLayer::Shadow].push_back(shadowedSkullRitem.get());

  auto mirrorRitem = std::make_unique<RenderItem>();
  mirrorRitem->World = MathHelper::Identity4x4();
  mirrorRitem->TexTransform = MathHelper::Identity4x4();
  mirrorRitem->ObjCBIndex = 6;
  mirrorRitem->Mat = materials_["Icemirror"].get();
  mirrorRitem->Geo = geometries_["RoomGeo"].get();
  mirrorRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  mirrorRitem->IndexCount = mirrorRitem->Geo->DrawArgs["Mirror"].IndexCount;
  mirrorRitem->StartIndexLocation =
      mirrorRitem->Geo->DrawArgs["Mirror"].StartIndexLocation;
  mirrorRitem->BaseVertexLocation =
      mirrorRitem->Geo->DrawArgs["Mirror"].BaseVertexLocation;
  render_layer[(int)RenderLayer::Mirrors].push_back(mirrorRitem.get());
  render_layer[(int)RenderLayer::Transparent].push_back(mirrorRitem.get());

  items_.push_back(std::move(floorRitem));
  items_.push_back(std::move(wallsRitem));
  items_.push_back(std::move(skullRitem));
  items_.push_back(std::move(reflectedSkullRitem));
  items_.push_back(std::move(reflectedFloor));
  items_.push_back(std::move(shadowedSkullRitem));
  items_.push_back(std::move(mirrorRitem));
}

void DxGraphics::BuildPSOs_Room() {
  //
  // 用于不透明的对象
  //
  D3D12_GRAPHICS_PIPELINE_STATE_DESC opaque_desc;
  ZeroMemory(&opaque_desc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));

  opaque_desc.InputLayout = {
      input_layout_.data(), static_cast<UINT>(input_layout_.size())};
  opaque_desc.pRootSignature = root_signature_.Get();
  opaque_desc.VS = {
      reinterpret_cast<BYTE*>(shaders_["StandardVS"]->GetBufferPointer()),
      shaders_["StandardVS"]->GetBufferSize()};
  opaque_desc.PS = {
      reinterpret_cast<BYTE*>(shaders_["OpaquePS"]->GetBufferPointer()),
      shaders_["OpaquePS"]->GetBufferSize()};
  opaque_desc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
  // opaque_desc.RasterizerState.FillMode = D3D12_FILL_MODE_WIREFRAME;
  opaque_desc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
  opaque_desc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
  opaque_desc.SampleMask = UINT_MAX;
  opaque_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
  opaque_desc.NumRenderTargets = 1;
  opaque_desc.RTVFormats[0] = back_buffer_format_;
  opaque_desc.SampleDesc.Count = GetMsaaSampleCount();
  opaque_desc.SampleDesc.Quality = GetMsaaSampleQuality();
  opaque_desc.DSVFormat = depth_stencil_format_;
  // 用于不透明的对象
  HASSERT(device_->CreateGraphicsPipelineState(
      &opaque_desc, IID_PPV_ARGS(&pipelines_["Opaque"])));

  //
  // 用于透明的Pipeline
  //
  D3D12_GRAPHICS_PIPELINE_STATE_DESC transparent_desc = opaque_desc;
  // 用于透明混合
  D3D12_RENDER_TARGET_BLEND_DESC blend_desc = {};
  blend_desc.BlendEnable = true;
  blend_desc.LogicOpEnable = false;
  blend_desc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
  blend_desc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
  blend_desc.BlendOp = D3D12_BLEND_OP_ADD;
  blend_desc.SrcBlendAlpha = D3D12_BLEND_ONE;
  blend_desc.DestBlendAlpha = D3D12_BLEND_ZERO;
  blend_desc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
  blend_desc.LogicOp = D3D12_LOGIC_OP_NOOP;
  blend_desc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
  transparent_desc.BlendState.RenderTarget[0] = blend_desc;
  HASSERT(device_->CreateGraphicsPipelineState(
      &transparent_desc, IID_PPV_ARGS(&pipelines_["Transparent"])));

  //
  // 用于镜面的Pipeline。用于将镜面绘制进模板缓冲区
  //
  CD3DX12_BLEND_DESC mirror_blend_state(D3D12_DEFAULT);
  // RenderTargetWriteMask = 0 这个东西就是禁止所有颜色写入到缓冲区。
  mirror_blend_state.RenderTarget[0].RenderTargetWriteMask = 0;
  D3D12_DEPTH_STENCIL_DESC mirror_dss = {};
  mirror_dss.DepthEnable = true;
  // 这一行是禁止写入深度缓冲区，因为我们仅需要写模板缓冲区。
  mirror_dss.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
  mirror_dss.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
  mirror_dss.StencilEnable = TRUE;
  mirror_dss.StencilReadMask = 0xFF;
  mirror_dss.StencilWriteMask = 0xFF;
  mirror_dss.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
  mirror_dss.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
  mirror_dss.FrontFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE;
  mirror_dss.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
  mirror_dss.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
  mirror_dss.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
  mirror_dss.BackFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE;
  mirror_dss.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
  // 创建Pipeline
  D3D12_GRAPHICS_PIPELINE_STATE_DESC mark_mirror_desc = opaque_desc;
  // 替换掉BlendState
  mark_mirror_desc.BlendState = mirror_blend_state;
  mark_mirror_desc.DepthStencilState = mirror_dss;
  HASSERT(device_->CreateGraphicsPipelineState(
      &mark_mirror_desc, IID_PPV_ARGS(&pipelines_["MarkStencilMirrors"])));

  //
  // 用于绘制镜面内物体的pipeline。
  //
  D3D12_DEPTH_STENCIL_DESC reflections_dss;
  reflections_dss.DepthEnable = true;
  reflections_dss.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
  reflections_dss.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
  reflections_dss.StencilEnable = true;
  reflections_dss.StencilReadMask = 0xFF;
  reflections_dss.StencilWriteMask = 0xFF;

  reflections_dss.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
  reflections_dss.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
  // 看这两行的不同
  reflections_dss.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
  reflections_dss.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
  reflections_dss.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
  reflections_dss.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
  reflections_dss.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
  reflections_dss.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;

  D3D12_GRAPHICS_PIPELINE_STATE_DESC reflections_desc = opaque_desc;
  reflections_desc.DepthStencilState = reflections_dss;
  reflections_desc.RasterizerState.CullMode = D3D12_CULL_MODE_BACK;
  reflections_desc.RasterizerState.FrontCounterClockwise = true;
  HASSERT(device_->CreateGraphicsPipelineState(
      &reflections_desc, IID_PPV_ARGS(&pipelines_["DrawStencilReflections"])));

  //
  // 用于绘制平面阴影pipeline。
  //
  D3D12_DEPTH_STENCIL_DESC shadow_dss;
  shadow_dss.DepthEnable = true;
  shadow_dss.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
  shadow_dss.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
  shadow_dss.StencilEnable = true;
  shadow_dss.StencilReadMask = 0xff;
  shadow_dss.StencilWriteMask = 0xff;

  shadow_dss.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
  shadow_dss.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
  shadow_dss.FrontFace.StencilPassOp = D3D12_STENCIL_OP_INCR;
  shadow_dss.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
  shadow_dss.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
  shadow_dss.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
  shadow_dss.BackFace.StencilPassOp = D3D12_STENCIL_OP_INCR;
  shadow_dss.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;

  // 这个是继承到Transparent的PSOriasis
  D3D12_GRAPHICS_PIPELINE_STATE_DESC shadow_desc = transparent_desc;
  shadow_desc.DepthStencilState = shadow_dss;
  HASSERT(device_->CreateGraphicsPipelineState(
      &shadow_desc, IID_PPV_ARGS(&pipelines_["Shadow"])));
}

void DxGraphics::BuildFrameResources_Room() {
  FrameResourceDesc desc = {};
  desc.PassCount = 2;
  desc.ObjectCount = static_cast<UINT>(items_.size());
  desc.MaterialCount = static_cast<UINT>(materials_.size());
  desc.WaveVertexCount = 0;

  for (int i = 0; i < FRAME_RESOURCE_COUNT; i++) {
    frame_resources_.push_back(
        std::make_unique<FrameResource>(device_.Get(), &desc));
  }
}

void DxGraphics::Draw_Room() {
  auto cmd = command_list_.Get();

  // 使用MainPass绘制不透明的常规对象
  auto pass_cb = current_frame_resource_->PassCB->Resource();
  cmd->SetGraphicsRootConstantBufferView(2, pass_cb->GetGPUVirtualAddress());
  DrawRenderItems(cmd, render_layer[(int)RenderLayer::Opaque]);

  // 在模板缓冲区中用值1标记可见的镜像像素
  cmd->OMSetStencilRef(1);
  // 用MarkStencilMirrors的PSO来绘制镜面
  cmd->SetPipelineState(pipelines_["MarkStencilMirrors"].Get());
  // 仅绘制镜子（这并不是绘制像素，而是将镜子绘制进模板缓冲区）
  DrawRenderItems(cmd, render_layer[(int)RenderLayer::Mirrors]);

  // 仅在镜像中绘制反射（仅针对模板缓冲区值为1的像素）。
  // 注意，我们必须提供一个不同的每通道常量缓冲区——一个带有反射灯光的缓冲区。
  // 设置PASS为反射光的PASS（我好像有根签名的直觉了）
  cmd->SetGraphicsRootConstantBufferView(
      2, pass_cb->GetGPUVirtualAddress() + 1 * MAIN_PASS_CB_SIZE);
  // 用DrawStencilReflections的PSO来绘制镜中显示的东西
  cmd->SetPipelineState(pipelines_["DrawStencilReflections"].Get());
  // 绘制镜中物体
  DrawRenderItems(cmd, render_layer[(int)RenderLayer::Reflected]);

  // 恢复MainPass和横版缓冲区的引用
  cmd->SetGraphicsRootConstantBufferView(2, pass_cb->GetGPUVirtualAddress());
  cmd->OMSetStencilRef(0);

  // 使用Transparent（透明管线）绘制镜子
  cmd->SetPipelineState(pipelines_["Transparent"].Get());
  DrawRenderItems(cmd, render_layer[(int)RenderLayer::Transparent]);

  // 绘制平面阴影
  cmd->SetPipelineState(pipelines_["Shadow"].Get());
  DrawRenderItems(cmd, render_layer[(int)RenderLayer::Shadow]);
}

} // namespace ifire